---
sidebar_position: 7
---

# Часть 6. Продвинутая работа с системой

:::info
Основная задача: научиться управлять процессами и создавать простейшую автоматизацию.
:::

## Автоматизируем сбор данных о системе с bash

1. Выведем LA:

   ```bash
   cat /proc/loadavg | awk '{ print $1,$2,$3" processes: "$4", last PID: "$5}'
   ```

   **LA, Load average** - среднее значение загрузки системы за некоторый период времени, как правило, отображается в виде трёх значений, которые представляют собой усредненные сглаженные величины за последние 1, 5 и 15 минут. Вычисляется как длина очереди выполнения в операционной системе, где единица означает, что очередь заполнена, а значение выше единицы — что есть процессы, которые ожидают своей очереди на выполнение. Содержатся в файле `/proc/loadavg`.

   Учитывает как running процессы, так и процессы в `uninterraprable sleep` (ожидают выполнения системного вызова, как правило долгие это read/write, т.е. IO).

2. Выведем список сетевых интерфейсов в нужном формате

   ```bash
   ss -4tuln | подставьте что нужно, можно цепочку из нескольких команд | column -t
   ```

   ```bash
   127.0.0.53%lo  53
   127.0.0.53%lo  53
   0.0.0.0        22
   ```

3. Выведем список IP-адресов на хосте:

   ```bash
   ip a | grep -E 'inet ' | awk '{ print $2 }'
   ```

4. Обернем это все в bash-скрипт `/root/beholder.sh`, добавим `date + "%d.%m.%Y %H:%M:%S"` и информацию о занятом месте и списке пользователей (самостоятельно). Попробуем циклы, запишем в скрипт:

   ```bash
   for i in $@
   do
       echo "Next search by name:"
       ps aux | grep $i
       echo
   done
   ```

5. Запустим. Должны получить что-то вроде:

   ![beholder](./images/beholder.png)

## Делаем сбор данных регулярным с cron

1. Впишем в cron новое правило и ждем минуту.

   ```bash
   echo '* * * * * root /root/beholder.sh >> /tmp/beholder-output' >
   /etc/cron.d/beholder
   ```

2. Смотрим файл `cat /tmp/beholder-output`

   Можем попробовать поменять параметры, сервис [crontab.guru](https://crontab.guru/) в помощь.

   Краткое описание есть в файле конфига `/etc/crontab`:

   ![image57](./images/image57.jpg)

   Исходя из этого конфига следует, что в `/etc/cron.{hourly,daily, weekly,monthly}/` лежат скрипты, запускающиеся соответственно раз в день, в неделю и в месяц. Дополнительно Cron читает конфиги из `/etc/cron.d/`, где лежат общие файлы с расписанием запуска.
   Кроме того, для пользователей задаются персональные cron-конфиги. Их можно отредактировать с помощью утилиты `crontab -e`. Они отличаются от `/etc/crontab` тем, что там опускается поле пользователя, запускаться будет от того, который себе задал это в настройках.

## Пишем свой systemd-сервис

Процесс не может взяться из ниоткуда: его обязательно должен запустить какой-то процесс. Процесс, запущенный другим процессом, называется дочерним (child) процессом или потомком. Процесс, который запустил процесс называется родительским (parent), родителем или просто - предком. У каждого процесса есть два атрибута - **PID** (Process ID) - идентификатор процесса и **PPID** (Parent Process ID) - идентификатор родительского процесса.

Процессы создают иерархию в виде дерева. Самым "главным" предком, то есть процессом, стоящим на вершине этого дерева, является процесс `systemd` или `init` (`PID = 1`). Условно можно считать, что планировщик ядра имеет `PID = 0`.

Собственно для управления древом процессов и создали `systemd`. Подробнее о процесса и их состояниях читайте в [статье](https://github.com/iu5git/linux-course/blob/master/Wiki.md#%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81-%D0%B2-gnulinux).

[systemd](https://ru.wikipedia.org/wiki/Systemd) — подсистема инициализации и управления службами в GNU/Linux, фактически вытеснившая в 2010-е годы традиционную подсистему [init](https://ru.wikipedia.org/wiki/Init). Основная особенность — интенсивное распараллеливание запуска служб в процессе загрузки системы, что позволяет существенно ускорить запуск операционной системы. Основная единица управления — unit (модуль), одним из типов модулей являются "сервисы" — аналог демонов — наборы процессов, запускаемые и управляемые средствами подсистемы и изолируемые [контрольными группами](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D0%B3%D1%80%D1%83%D0%BF%D0%BF%D0%B0_(Linux)). Подробная [статья](https://linux-notes.org/pishem-systemd-unit-fajl/) об systemd-unit-ах.

Создадим свой systemd-unit:

```bash
nano /etc/systemd/system/myhttp.service

# Создадим для него WorkingDirectory
mkdir -p /root/www

# Перезагрузим конфигурацию
systemd systemctl daemon-reload
```

Содержимое `.service` файла:

```bash
[Unit]
Description=MyHTTP Server
Documentation=http://example.org/
After=network.target

[Service]
WorkingDirectory=/root/www
ExecStart=/usr/bin/python3 -m http.server 8000
KillMode=mixed
Restart=on-failure
Type=simple

[Install]
WantedBy=multi-user.target
Alias=myhttpd.service
```

Кратко разберем что тут написано:

- Секция `[Unit]`:

  - `Description` - описание, отображается в `systemctl status [service]`
  - `Documentation` - ссылка на документацию, обязательно в виде URL
  - `After` - после каких целей/сервисов следует запускать данный unit

- Секция `[Service]`:

  - `WorkingDirectory` - вызовет системный вызов `chdir` перед исполнением каких-либо команд сервиса, должна существовать
  - `ExecStart` - команда, которая запустится при вызове `systemctl start [service]`
  - `KillMode` - управляет тем, как процесс должен быть завершен, `mixed` - посылает `SIGTERM` основному процессу и `SIGKILL` дочерним
  - `Restart` - необходимо ли перезапускатьсервис в случае завершения процесса (в примере - только при ненулевом rc)
  - `Type` - настраивает процедуру запуска и отслеживания процесса, `simple` ожидает, что основным станет процесс, запущенный ExecStart командой и сервис будет работать до тех пор, пока жив основной процесс.

- Секция `[Install]`:

  - `WantedBy` - какая цель триггерит запуск, если сервис `enabled`.
  - `Alias` - альтернативное имя сервиса. `systemctl status [alias]` равнозначен `systemctl start [service]`.

Запустим, добавим в автозапуск:

```bash
systemctl start myhttp
systemctl enable myhttp
```

Проверяем работу с помощью `curl` и `ss`.

```bash
curl http://127.0.0.1:8000/
ss -tl | grep 8000
```

:::info
Для ответа на контрольный вопрос про процесс загрузки системы
также нужно ознакомиться с [материалом](https://github.com/iu5git/linux-course/blob/master/Wiki.md#%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81-%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B8-gnulinux).
:::

## Запускаем процесс внутри нового пространства имен

Посмотрим список доступных пространств имен:

```bash
lsns
ip netns
```

Цикл [статей](https://habr.com/ru/post/458462/) на Habr про пространства имен.

Создаем сетевой namespace:

```bash
ip netns add myhttp
```

Теперь запустим внутри пару команд `ip netns exec <netns> <cmd> ...`:

```bash
ip netns exec myhttp ip link set dev lo up
ip netns exec myhttp /usr/bin/python3 -m http.server 8080 &
```

Проверим:

```bash
ss -tul4n
ip netns exec myhttp ss -tul4n
```

Запустим tcpdump внутри netns-а:

```bash
ip netns exec myhttp tcpdump -i any host 127.0.0.1
```

Проверим:

```bash
ip netns exec myhttp curl http://127.0.0.1:8080
ip netns exec myhttp curl http://127.0.0.1:8000
curl http://127.0.0.1:8080
curl http://127.0.0.1:8000
```

Таким образом, у нас получилось разнести 2 разных сервера в разные сетевые пространства имен. Мы даже можем запустить их на одинаковом порту.

## Установка docker и запуск hello-world

[Установка Docker в Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04-ru)

```bash
sudo apt install apt-transport-https ca-certificates curl software-properties-
common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64]
https://download.docker.com/linux/ubuntu focal stable"
sudo apt update
sudo apt install docker-ce
sudo systemctl status docker
```

Запустим [hello-world](https://hub.docker.com/_/hello-world):

```bash
docker run hello-world
```

## Дополнительное

1. Показать soft-лимиты (`ulimit`) и hard-лимиты, пояснить как поняли, в чем разница, как изменить?

   Хорошая [статья](https://andreyex.ru/linux/komandy-linux-i-komandy-shell/upravlenie-resursami-sistemy-s-pomoshhyu-komandy-ulimit/) о лимитах.

   :::info
   Примечание: это может оказаться жизненно важным знанием в случае появления высоких нагрузок на вашем сервере. Ошибка "Too Many Open Files" может появляться как раз из-за ограничений в лимитах, по-умолчанию они довольно небольшие (1024 для open files).
   :::

   Пример вывода `ulimit` для пары систем (ВМ и реальный Raspberry-Pi сервер):

   ![image59](./images/image59.jpg)

   Посмотрим лимиты для конкретного процесса, тоже бывает полезно:

   ```bash
   # Некоторые программы кладут свой Process ID (PID) в /var/run
   cat /var/run/sshd.pid # Выведет PID sshd

   # Выведем лимиты процесса sshd с помощью procfs
   cat /proc/$(cat /var/run/sshd.pid)/limits
   # P.S. Про procfs подробно поговорим во второй лабораторной работе
   ```

   ![image60](./images/image60.jpg)

   Занятное наблюдение работы лимитов:

   ![image62](./images/image62.jpg)

2. Настроить `/root/.bashrc` файл, применяющийся при создании новой оболочки для root пользователя.

   Хорошая [статья](https://evilinside.ru/razbiraemsya-s-fajlami-etc-profile-i-etc-bashrc/) о `bash_profile` и `bashrc`.

   [Пример](https://www.opennet.ru/docs/RUS/bash_scripting_guide/a15124.html) большого и сложного `.bashrc` файла.

   PS1 - переменная для изменения вида приглашения, например такое значение красит в красный:

   ```bash
   PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\
   [\033[01;31m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w \$\[\033[00m\] '
   ```

   Синонимы (alias-ы) для подкрашивания вывода команд:

   ```bash
   # enable color support of ls and also add handy aliases
   if [ -x /usr/bin/dircolors ]; then
       test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval
   "$(dircolors -b)"
       alias ls='ls --color=auto'
       alias dir='dir --color=auto'
       alias vdir='vdir --color=auto'
       alias grep='grep --color=auto'
       alias fgrep='fgrep --color=auto'
       alias egrep='egrep --color=auto'
   fi
   ```

   Просто удобные общепринятые сокращения:

   ```bash
   # some more ls aliases
   alias ll='ls -l'
   alias la='ls -A'
   alias l='ls -lA'
   ```

   Переопределения стандартных команд с ключами, чтобы спрашивали подтверждение:

   ```bash
   # Some more alias to avoid making mistakes:
   alias rm='rm -i'
   alias cp='cp -i'
   alias mv='mv -i'
   ```

   ![image64](./images/image64.jpg)

   ![image65](./images/image65.jpg)

3. Создайте еще один мгновенный снимок ВМ, как в пункте 8 раздела 1.1. Чтобы не случалось _"упс, у меня все работало, а вот сейчас перестало"_.

:::info
Далее в лабораторных рекомендуется использовать способ работы с системой по ssh и запускать ВМ в фоновом режиме (без интерфейса вообще).
:::
